#include "Avalon/IOTests/PipedMessageReaderWriterTester.hpp"
#include "Avalon/IO/Buffer.hpp"
#include "Avalon/IO/PipedMessageReader.hpp"
#include "Avalon/IO/PipedMessageWriter.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::IO::Tests;
using namespace Avalon::Threading;
using namespace boost;
using namespace std;

void PipedMessageReaderWriterTester::TestSingleWriteSingleRead() {
  PipedMessageReader reader;
  PipedMessageWriter writer(&reader);
  string message = "hello world";
  Async<void> writeResult;
  writer.Write(message.c_str(), static_cast<int>(message.size()),
    Store(writeResult));
  Buffer readData;
  Async<int> readResult;
  reader.Read(&readData, Store(readResult));
  CPPUNIT_ASSERT(readResult.Get() == message.size());
  CPPUNIT_ASSERT(readData.GetSize() == message.size());
  CPPUNIT_ASSERT(string(readData.GetData(), readData.GetSize()) == message);
}

void PipedMessageReaderWriterTester::TestMultipleWriteSingleRead() {
  PipedMessageReader reader;
  PipedMessageWriter writer(&reader);
  string firstFragment = "hello";
  string secondFragment = " world";
  Async<void> writeResult;
  writer.Write(firstFragment.c_str(), static_cast<int>(firstFragment.size()),
    Store(writeResult));
  writeResult.Reset();
  writer.Write(secondFragment.c_str(), static_cast<int>(secondFragment.size()),
    Store(writeResult));
  Async<int> readResult;
  Buffer readData;
  reader.Read(&readData, Store(readResult));
  CPPUNIT_ASSERT(readResult.Get() == firstFragment.size());
  CPPUNIT_ASSERT(readData.GetSize() == firstFragment.size());
  CPPUNIT_ASSERT(string(readData.GetData(), readData.GetSize()) ==
    firstFragment);
}

void PipedMessageReaderWriterTester::TestMultipleWriteRead() {
  PipedMessageReader reader;
  PipedMessageWriter writer(&reader);
  string firstFragment = "hello";
  string secondFragment = " world";

  // Write then read the first fragment.
  Async<void> writeResult;
  writer.Write(firstFragment.c_str(), static_cast<int>(firstFragment.size()),
    Store(writeResult));
  writeResult.Reset();
  Buffer readData;
  Async<int> readResult;
  reader.Read(&readData, Store(readResult));
  CPPUNIT_ASSERT(readResult.Get() == firstFragment.size());
  CPPUNIT_ASSERT(readData.GetSize() == firstFragment.size());
  CPPUNIT_ASSERT(string(readData.GetData(), readData.GetSize()) ==
    firstFragment);
  readResult.Reset();
  readData.Reset();

  // Write and read the second fragment.
  writer.Write(secondFragment.c_str(),
    static_cast<int>(secondFragment.size()), Store(writeResult));
  writeResult.Reset();
  reader.Read(&readData, Store(readResult));
  CPPUNIT_ASSERT(readResult.Get() == secondFragment.size());
  CPPUNIT_ASSERT(readData.GetSize() == secondFragment.size());
  CPPUNIT_ASSERT(string(readData.GetData(), readData.GetSize()) ==
    secondFragment);
}

void PipedMessageReaderWriterTester::TestMultipleWritesThenMultipleReads() {
  PipedMessageReader reader;
  PipedMessageWriter writer(&reader);
  string firstFragment = "hello";
  string secondFragment = " world";

  // Write both fragments.
  Async<void> writeResult;
  writer.Write(firstFragment.c_str(), static_cast<int>(firstFragment.size()),
    Store(writeResult));
  writeResult.Reset();
  writer.Write(secondFragment.c_str(), static_cast<int>(secondFragment.size()),
    Store(writeResult));
  writeResult.Reset();

  // Read the first fragment.
  Async<int> readResult;
  Buffer firstFragmentRead;
  reader.Read(&firstFragmentRead, Store(readResult));
  CPPUNIT_ASSERT(readResult.Get() == firstFragment.size());
  CPPUNIT_ASSERT(firstFragmentRead.GetSize() == firstFragment.size());
  CPPUNIT_ASSERT(string(firstFragmentRead.GetData(), firstFragment.size()) ==
    firstFragment);
  readResult.Reset();

  // Read the second fragment.
  Buffer secondFragmentRead;
  reader.Read(&secondFragmentRead, Store(readResult));
  CPPUNIT_ASSERT(readResult.Get() == secondFragment.size());
  CPPUNIT_ASSERT(secondFragmentRead.GetSize() == secondFragment.size());
  CPPUNIT_ASSERT(string(secondFragmentRead.GetData(),
    secondFragmentRead.GetSize()) == secondFragment);
}
